package ipcache

import (
	"duanju-mq/pkg/config"
	iputil "duanju-mq/pkg/net/ip"
	"duanju-mq/pkg/redis"
	"duanju-mq/pkg/region"
	"errors"
	"strconv"
	"strings"
)

var (
	fKey = config.String("ip.redis.key", "IP", "")
	rc   = redis.NewClientFromConfig("ip.redis")
)

func Redis() *redis.Client { return rc }

type ErrOutOfRange struct {
	IP string
}

func (e *ErrOutOfRange) Error() string {
	return "ip out of range: " + e.IP
}

func GetRegion(ip string) (*region.Region, error) {
	ipn, err := iputil.IP2N(ip)
	if err != nil {
		return nil, err
	}
	vs, err := rc.Exec("ZRANGEBYSCORE", rc.FK(*fKey), ipn, "+inf", "LIMIT", 0, 1).Strings()
	if err != nil {
		return nil, err
	}
	if len(vs) == 0 {
		return nil, &ErrOutOfRange{IP: ip}
	}

	ss := strings.Split(vs[0], "|")
	if len(ss) < 4 {
		return nil, errors.New("wrong data")
	}

	startIP, err := strconv.Atoi(ss[1])
	if err != nil {
		return nil, err
	}
	endIP, err := strconv.Atoi(ss[2])
	if err != nil {
		return nil, err
	}
	if int(ipn) < startIP || int(ipn) > endIP {
		return nil, &ErrOutOfRange{IP: ip}
	}

	return region.Parse(ss[3])
}
func GetRegionTimeZone(ip string) (*region.RegionTimeZone, error) {
	data, err := GetData(ip)
	if err != nil {
		return nil, err
	}
	r, err := region.Parse(data.Region)
	if err != nil {
		return nil, err
	}
	return &region.RegionTimeZone{
		Region:   r,
		TimeZone: data.TimeZone,
	}, nil
}

type Data struct {
	StartIP  int
	EndIP    int
	Region   string
	Lat      string
	Lon      string
	TimeZone string
}

func GetData(ip string) (*Data, error) {
	ipn, err := iputil.IP2N(ip)
	if err != nil {
		return nil, err
	}
	vs, err := rc.Exec("ZRANGEBYSCORE", rc.FK(*fKey), ipn, "+inf", "LIMIT", 0, 1).Strings()
	if err != nil {
		return nil, err
	}
	if len(vs) == 0 {
		return nil, &ErrOutOfRange{IP: ip}
	}

	ss := strings.Split(vs[0], "|")
	if len(ss) < 6 {
		return nil, errors.New("wrong data")
	}
	startIP, err := strconv.Atoi(ss[1])
	if err != nil {
		return nil, err
	}
	endIP, err := strconv.Atoi(ss[2])
	if err != nil {
		return nil, err
	}
	if int(ipn) < startIP || int(ipn) > endIP {
		return nil, &ErrOutOfRange{IP: ip}
	}

	return &Data{
		StartIP:  startIP,
		EndIP:    endIP,
		Region:   ss[3],
		Lat:      ss[4],
		Lon:      ss[5],
		TimeZone: ss[6],
	}, nil
}
